import {
	EventDispatcher,
	Mesh,
	MeshNormalMaterial,
	MeshStandardMaterial,
	SphereGeometry,
	Vector2,
	Vector3,
} from 'three'
import LinkedList from './LinkedList'
import { RoundedBoxGeometry } from 'three/examples/jsm/geometries/RoundedBoxGeometry'
import ListNode from './ListNode'
import Entity from './Entity'

const NODE_GEOMETRY = new RoundedBoxGeometry(0.9, 0.9, 0.9, 5, 0.1) // 用threejs内置函数创建圆角四方块
const NODE_MATERIAL = new MeshStandardMaterial({
	color: 0xff470a,
}) // 创建材质并给初始颜色

const UP = new Vector3(0, 0, -1)
const DOWN = new Vector3(0, 0, 1)
const LEFT = new Vector3(-1, 0, 0)
const RIGHT = new Vector3(1, 0, 0)
export default class Snake extends EventDispatcher {
	direction = RIGHT  // 初始方向朝右
  indexes = []       // 存储蛇身占用的格子坐标
  speedInterval = 240 // 移动速度（数值越大越慢）

	constructor({ scene, resolution = new Vector2(10, 10), color, mouthColor }) {
		super()
		// 把游戏场景、地图尺寸、颜色存起来
		this.scene = scene
		this.resolution = resolution
		this.mouthColor = mouthColor
		// 如果有颜色参数，给所有蛇节点刷颜色
		if (color) {
			NODE_MATERIAL.color.set(color)
		}

		this.init() // 开始组装蛇
	}

	get head() {
		return this.body.head
	}

	get end() {
		return this.body.end
	}

	//组装蛇头
	createHeadMesh() {
		const headMesh = this.body.head.data.mesh // 获取蛇头模型

		// 造左眼：白眼球+黑眼珠
		const leftEye = new Mesh(
			new SphereGeometry(0.2, 10, 10),
			new MeshStandardMaterial({ color: 0xffffff })
		)
		leftEye.scale.x = 0.1
		leftEye.position.x = 0.5
		leftEye.position.y = 0.12
		leftEye.position.z = -0.1

		let leftEyeHole = new Mesh(
			new SphereGeometry(0.22, 10, 10),
			new MeshStandardMaterial({ color: 0x333333 })
		)
		leftEyeHole.scale.set(1, 0.6, 0.6)
		leftEyeHole.position.x += 0.05
		leftEye.add(leftEyeHole)

		// 造右眼：同上
		const rightEye = leftEye.clone()

		rightEye.position.x = -0.5
		rightEye.rotation.y = Math.PI

		// 造嘴巴：紫色圆角矩形
		const mouthMesh = new Mesh(
			new RoundedBoxGeometry(1.05, 0.1, 0.6, 5, 0.1),
			new MeshStandardMaterial({
				color: this.mouthColor, //0x614bdd,
			})
		)

		mouthMesh.rotation.x = -Math.PI * 0.07
		mouthMesh.position.z = 0.23
		mouthMesh.position.y = -0.19

		this.mouth = mouthMesh

		// 把零件装到蛇头上
		headMesh.add(rightEye, leftEye, mouthMesh)
		/* 调整头部朝向当前方向 */
		headMesh.lookAt(headMesh.position.clone().add(this.direction))
	}

	init() {
		// 重置方向
		this.direction = RIGHT
		this.iMoving = null

		// 造第一个蛇头节点（放在地图中心）
		const head = new ListNode(new SnakeNode(this.resolution))

		head.data.mesh.position.x = this.resolution.x / 2
		head.data.mesh.position.z = this.resolution.y / 2
		this.body = new LinkedList(head)

		this.createHeadMesh()

		this.indexes.push(this.head.data.getIndexByCoord())
		// 添加初始三节身体（类似火车车厢）
		for (let i = 0; i < 3; i++) {
			const position = this.end.data.mesh.position.clone()
			position.sub(this.direction) // 每节身体往后挪一格
			this.addTailNode() // 挂载到链表末尾
			this.end.data.mesh.position.copy(position)

			this.indexes.push(this.end.data.getIndexByCoord())
		}

		head.data.in()
		this.scene.add(head.data.mesh)
	}

	setDirection(keyCode) {
		let newDirection
		// 根据按键计算新方向（上下左右）
		switch (keyCode) {
			case 'ArrowUp':
			case 'KeyW':
				newDirection = UP
				break
			case 'ArrowDown':
			case 'KeyS':
				newDirection = DOWN
				break
			case 'ArrowLeft':
			case 'KeyA':
				newDirection = LEFT
				break
			case 'ArrowRight':
			case 'KeyD':
				newDirection = RIGHT
				break
			default:
				return
		}

		const dot = this.direction.dot(newDirection)
		// 禁止180度掉头（比如正在右走不能直接左转）
		if (dot === 0) {
			this.newDirection = newDirection
		}
	}

	// 每帧判断
	update() {
		// 应用新方向
		if (this.newDirection) {
			this.direction = this.newDirection
			this.newDirection = null
		}
		let currentNode = this.end

		// 处理吃糖果后的尾巴生长
		if (this.end.data.candy) {
			this.end.data.candy = null
			this.end.data.mesh.scale.setScalar(1)

			this.addTailNode()
		}

		// 身体像波浪一样跟随头部移动
		while (currentNode.prev) {
			const candy = currentNode.prev.data.candy
			if (candy) {
				currentNode.data.candy = candy
				currentNode.data.mesh.scale.setScalar(1.15)
				currentNode.prev.data.candy = null
				currentNode.prev.data.mesh.scale.setScalar(1)
			}

			const position = currentNode.prev.data.mesh.position
			// 每节身体移动到前一节的位置
			currentNode.data.mesh.position.copy(position)

			currentNode = currentNode.prev // 往前遍历
		}
		const headPos = currentNode.data.mesh.position
		headPos.add(this.direction)
		const headMesh = this.body.head.data.mesh
		headMesh.lookAt(headMesh.position.clone().add(this.direction))
		// 移动头部
		if (headPos.z < 0) {
			headPos.z = this.resolution.y - 1
		} else if (headPos.z > this.resolution.y - 1) {
			headPos.z = 0
		}
		// 边界穿越处理（从地图左边消失就从地图右边出来...其他边界类似）
		if (headPos.x < 0) {
			headPos.x = this.resolution.x - 1
		} else if (headPos.x > this.resolution.x - 1) {
			headPos.x = 0
		}

		this.updateIndexes()
		// 向上抛出事件
		this.dispatchEvent({ type: 'updated' })
	}

	// 死亡
	die() {
		let node = this.body.head
		// 移除所有身体部件
		do {
			this.scene.remove(node.data.mesh) // 从场景删除模型
			node = node.next
		} while (node)

		this.init() // 重新初始化
		this.addEventListener({ type: 'die' }) // 向上抛出死亡事件
	}

	checkSelfCollision() {
		// 检查头部坐标是否和身体坐标重叠
		const headIndex = this.indexes.pop()
		const collide = this.indexes.includes(headIndex)
		this.indexes.push(headIndex)

		return collide // 撞到自己返回true
	}

	checkEntitiesCollision(entities) {
		// 检查头部坐标是否和障碍物坐标重叠
		const headIndex = this.indexes.at(-1)

		const entity = entities.find(
			(entity) => entity.getIndexByCoord() === headIndex
		)

		return !!entity // 撞到障碍物返回true
	}


	// 更新蛇身所有节点的网格坐标索引，用于碰撞检测等需要知道蛇身位置的功能
	updateIndexes() {
		// 清空旧索引（相当于擦除之前的身体痕迹）
		this.indexes = []
		// 从蛇尾开始遍历（相当于从火车最后一节车厢开始检查）
		let node = this.body.end
		// 循环向前遍历所有节点（直到没有前一节车厢）
		while (node) {
			// 获取当前节点的网格坐标（比如把3D坐标转换为地图网格的[x,y]）
    	// 假设地图是10x10网格，坐标(5.3, 0, 5.7)会被转换为索引[5,5]
    	// 将索引推入数组（记录这节身体占据的格子）
			this.indexes.push(node.data.getIndexByCoord())
			// 移动到前一节身体（向蛇头方向移动）
			node = node.prev
			// 最终得到的indexes数组示例：
		  // [[3,5], [4,5], [5,5]] 表示蛇身占据这三个网格
		  // 其中最后一个元素[5,5]是蛇头位置
		}
	}

	// 添加尾部节点
	addTailNode(position) {
		const node = new ListNode(new SnakeNode(this.resolution))

		if (position) {
			node.data.mesh.position.copy(position)
		} else {
			node.data.mesh.position.copy(this.end.data.mesh.position)
		}

		this.body.addNode(node)
		node.data.in()
		this.scene.add(node.data.mesh)
	}
}
// 蛇身体节点类
class SnakeNode extends Entity {
	constructor(resolution) {
		const mesh = new Mesh(NODE_GEOMETRY, NODE_MATERIAL)
		super(mesh, resolution)
	}
}
